package com.demo.webflux.spring5core.framework.aop.support;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.demo.webflux.spring5core.framework.aop.AopConfig;
import com.demo.webflux.spring5core.framework.aop.aspect.AfterReturningAdvice;
import com.demo.webflux.spring5core.framework.aop.aspect.AfterThrowingAdvice;
import com.demo.webflux.spring5core.framework.aop.aspect.MethodBeforeAdvice;

import feign.Feign;

/**主要用来解析和封装aop配置
 * @author zhangsulei
 *
 */
public class AdvisedSupport {

	private Class targetClass;
	private Object target;
	private Pattern pointCutClassPattern;

	private transient Map<String, List<Object>> methodCache = new HashMap<>();

	private AopConfig config;

	public AdvisedSupport(AopConfig config) {
		this.config = config;
	}

	public Class getTargetClass() {
		return targetClass;
	}

	public void setTargetClass(Class targetClass) {
		this.targetClass = targetClass;
	}

	public Object getTarget() {
		return target;
	}

	public void setTarget(Object target) {
		this.target = target;
	}

	/**根据aop配置，将需要回调的方法封装成一个拦截器链并返回提供给外部获取
	 * @param method
	 * @param targetClass
	 * @return
	 * @throws Exception
	 */
	public List<Object> getInterceptorsAndDynamicInterceptionAdvice(String key, Class<?> targetClass) throws Exception {
		List<Object> list = methodCache.get(key);
		if(list == null) {
			return new ArrayList<>();
		}
		return list;
	}

	/**判断目标类是否符合切面规则
	 * @return
	 */
	public boolean pointCutMatch() {
		return pointCutClassPattern.matcher(this.targetClass.toString()).matches();
	}

	public void parse() {
		// pointCut表达式
		String pointCut = config.getPointCut()
				.replaceAll("\\.", "\\\\.")
				.replaceAll("\\\\.\\*", ".*")
				.replaceAll("\\(", "\\\\(")
				.replaceAll("\\)", "\\\\)");

		String pointCutForClass = pointCut.substring(0, pointCut.lastIndexOf("\\(") - 4);
		pointCutClassPattern = Pattern.compile("class " + pointCutForClass.substring(pointCutForClass.lastIndexOf(" ") + 1));

		Pattern pattern = Pattern.compile(pointCut);

		// 能满足切面规则的类，添加到aop配置中
		try {
			Class aspectClass = Class.forName(config.getAspectClass());
			Map<String, Method> aspectMethods = new HashMap<>();
			for (Method m : aspectClass.getMethods()) {
				aspectMethods.put(m.getName(), m);
			}

			// 这里得到的方法都是原生方法
			for (Method m : targetClass.getMethods()) {
				String methodString = m.toString();
				if(methodString.contains("throws")) {
					methodString = methodString.substring(0, methodString.lastIndexOf("throws")).trim();
				}
				Matcher matcher = pattern.matcher(methodString);
				if(matcher.matches()) {
					List<Object> advicecs = new LinkedList<>();
					// 前置通知
					if(!(null == config.getAspectBefore() || "".equals(config.getAspectBefore().trim()))) {
						advicecs.add(new MethodBeforeAdvice(aspectMethods.get(config.getAspectBefore()), aspectClass.newInstance()));
					}
					// 后置通知
					if(!(null == config.getAspectAfter() || "".equals(config.getAspectAfter().trim()))) {
						advicecs.add(new AfterReturningAdvice(aspectMethods.get(config.getAspectAfter()), aspectClass.newInstance()));
					}
					// 异常通知
					if(!(null == config.getAspectAfterThrow() || "".equals(config.getAspectAfterThrow().trim()))) {
						AfterThrowingAdvice afterThrowingAdvice = new AfterThrowingAdvice(aspectMethods.get(config.getAspectAfterThrow()), aspectClass.newInstance());
						afterThrowingAdvice.setThrowingName(config.getAspectAfterThrowingName());
						advicecs.add(afterThrowingAdvice);
					}
					methodCache.put(Feign.configKey(targetClass, m), advicecs);
				}
			}
		}catch (Exception e) {
			e.printStackTrace();
		}
	}

}
